#pragma once

#include <YSimpleNetwork/YSimpleNetwork.h>
#include <YSimpleSystem/YLockedObject.h>
#include <assert.h>
#include <list>
#include <functional>

namespace ysimple_network
{

	struct YPacket
	{
	private:
		char*		m_data;
		size_t		m_written;
		size_t		m_capacitySize;
	public:
		YPacket(size_t capacitySize)
			: m_data(new char[capacitySize])
			, m_capacitySize(capacitySize)
			, m_written(0)
		{
			
		}
		~YPacket()
		{
			delete [] m_data;
		}

		char*		getrd()
		{
			return m_data;
		}

		size_t		written() const
		{
			return m_written;
		}

		void		written(size_t len)
		{
			m_written += len;
			assert(m_capacitySize >= m_written);
		}
	};

	
	struct YPacketBlock
	{
	private:
		PACKET_SPTR	m_packet;
		size_t		m_completed;
	public:
		YPacketBlock(PACKET_SPTR packet)
			: m_packet(packet)
			, m_completed(0)
		{

		}

		char*		getrd()
		{
			return m_packet->getrd() + m_completed;
		}

		size_t		written() const
		{
			return m_packet->written();
		}
		
		bool		completed() const
		{
			return completedLength() == m_packet->written();
		}

		void		completedLength(size_t completed)
		{
			m_completed += completed;
			assert(m_completed <= m_packet->written());
		}

		size_t		completedLength() const
		{
			return m_completed;
		}

		size_t		uncompletedLength() const
		{
			return m_packet->written() - m_completed;
		}
	};
	
	struct YMessageBlock
	{
	private:
		std::list<PACKETBLOCK_SPTR>				m_dataBlock;
		size_t										m_totalPacketLen;
	public:
		ysimple_system::YCriticalSection		m_criticalSection;
		YMessageBlock() 
			: m_totalPacketLen(0)
		{

		}
		bool empty() const
		{
			return m_dataBlock.empty();
		}

		void push(PACKETBLOCK_SPTR packet)
		{
			m_totalPacketLen += packet->written();
			m_dataBlock.push_back(packet);
		}

		void foreach(std::function<bool(PACKETBLOCK_SPTR&)> functor)
		{
			for (auto& packet : m_dataBlock)
			{
				if (false == functor(packet))
					break;
			}
		}

		bool complete(DWORD dwBytesTransfered)
		{
			m_totalPacketLen -= dwBytesTransfered;
			DWORD remainLen = dwBytesTransfered;
			while(m_dataBlock.empty() == false)
			{
				if (remainLen == 0)
					return false;

				PACKETBLOCK_SPTR& packet = *m_dataBlock.begin();

				size_t availableTransfered = __min(remainLen, packet->written()-packet->completedLength());
				packet->completedLength(availableTransfered);
				remainLen -= availableTransfered;
				
				if (packet->completed())
				{
					m_dataBlock.pop_front();
				}
			}
			
			return true;
		}
	};
}